#include "flash_driver.h"
#include <string.h>


/**
  * @brief  根据地址获取扇区号
  * @param  address: FLASH地址
  * @retval 扇区号
  */
uint32_t FLASH_GetSector(uint32_t address)
{
    if (address < SECTOR_1_START) return FLASH_SECTOR_0;
    else if (address < SECTOR_2_START) return FLASH_SECTOR_1;
    else if (address < SECTOR_3_START) return FLASH_SECTOR_2;
    else if (address < SECTOR_4_START) return FLASH_SECTOR_3;
    else if (address < SECTOR_5_START) return FLASH_SECTOR_4;
    else if (address < SECTOR_6_START) return FLASH_SECTOR_5;
    else if (address < SECTOR_7_START) return FLASH_SECTOR_6;
    else if (address < SECTOR_8_START) return FLASH_SECTOR_7;
    else if (address < SECTOR_9_START) return FLASH_SECTOR_8;
    else if (address < SECTOR_10_START) return FLASH_SECTOR_9;
    else if (address < SECTOR_11_START) return FLASH_SECTOR_10;
    else return FLASH_SECTOR_11;
}

/**
  * @brief  FLASH擦除函数
  * @param  addr: 起始地址
  * @param  len: 要擦除的数据长度
  * @retval HAL状态
  */
HAL_StatusTypeDef FLASH_Erase(uint32_t addr, uint32_t len)
{
    HAL_StatusTypeDef status = HAL_OK;
    uint32_t sector_error = 0;
    FLASH_EraseInitTypeDef erase_init;
    uint32_t start_sector, end_sector;
    uint32_t end_addr = addr + len - 1;
    
    // 参数检查
    if (len == 0) {
        return HAL_ERROR;
    }
    
    // 地址范围检查
    if (addr < FLASH_START_ADDR || end_addr > FLASH_END_ADDR) {
				printf("addr_err \n");
        return HAL_ERROR;
    }
    
    // 计算起始扇区和结束扇区
    start_sector = FLASH_GetSector(addr);
    end_sector = FLASH_GetSector(end_addr);
    
    // 检查是否跨越了太多扇区（安全限制）
    if (end_sector - start_sector > 6) { // 最多6个扇区
				printf("end_sector_err \n");
        return HAL_ERROR;
    }
    
    // 解锁FLASH
    HAL_FLASH_Unlock();
    
    // 配置擦除参数
    erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase_init.Banks = FLASH_BANK_1;
    erase_init.Sector = start_sector;
    erase_init.NbSectors = end_sector - start_sector + 1;
    erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 适用于2.7V-3.6V
    
    // 执行擦除
    status = HAL_FLASHEx_Erase(&erase_init, &sector_error);
    
    // 锁定FLASH
    HAL_FLASH_Lock();
    
    return status;
}


/**
  * @brief  读取FLASH数据
  * @param  data: 数据缓冲区指针
  * @param  len: 要读取的数据长度
  * @param  addr: 读取地址
  * @retval HAL状态
  */
HAL_StatusTypeDef FLASH_Read(uint8_t* data, uint32_t len, uint32_t addr)
{
    // 参数检查
    if (data == NULL || len == 0) {
        return HAL_ERROR;
    }
    
    // 地址范围检查
    if (addr < FLASH_START_ADDR || addr + len > FLASH_END_ADDR) {
        return HAL_ERROR;
    }
    
    // 直接内存拷贝读取
    memcpy(data, (void*)addr, len);
    
    return HAL_OK;
}

/**
  * @brief  计算对齐后的长度(4字节对齐)
  * @param  len: 原始长度
  * @retval 对齐后的长度
  */
uint32_t FLASH_GetAlignedLength(uint32_t len)
{
    return (len + 3) & ~3;  // 向上对齐到4的倍数
}

/**
  * @brief  写入FLASH数据(不擦除，需要提前擦除好)
  * @param  data: 数据缓冲区指针
  * @param  len: 要写入的数据长度
  * @param  addr: 写入地址(必须是4字节对齐)
  * @retval HAL状态
  */
HAL_StatusTypeDef FLASH_Write_NoErase(uint8_t* data, uint32_t len, uint32_t addr)
{
    HAL_StatusTypeDef status = HAL_OK;
    uint32_t aligned_len;
    uint8_t *aligned_data = NULL;
    uint32_t i;
    
    // 参数检查
    if (data == NULL || len == 0) {
        return HAL_ERROR;
    }
    
    // 地址范围检查
    if (addr < FLASH_START_ADDR || addr + len > FLASH_END_ADDR) {
        return HAL_ERROR;
    }
    
    // 地址对齐检查
    if (addr % 4 != 0) {
        return HAL_ERROR;
    }
    
    // 计算对齐长度
    aligned_len = FLASH_GetAlignedLength(len);
    
		aligned_data = data;

    // 解锁FLASH
    HAL_FLASH_Unlock();
    
    // 以4字节为单位写入数据
    for (i = 0; i < aligned_len; i += 4) {
        uint32_t word_data = *(uint32_t*)(aligned_data + i);
        
        // 编程FLASH
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, word_data);
        if (status != HAL_OK) {
            break;
        }
        
        // 验证写入的数据
        uint32_t read_data = *(uint32_t*)(addr + i);
        if (read_data != word_data) {
            status = HAL_ERROR;
            break;
        }
    }
    
    // 锁定FLASH
    HAL_FLASH_Lock();
    
    return status;
}
